﻿#region License
/**	
The MIT License (MIT)

Copyright (c) 2013 Philipp Schillinger, Michael Alekseew, Andreas Braun, Steeven Zeiß

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. 
 **/
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.GPU;
using Emgu.CV.VideoSurveillance;

namespace IndoorLocalization.Detection
{
	/// <summary>
	/// The motion debug window gives you the opportunity to see the different steps of the motion detection in seperated pictures.
	/// Also you can set the parameters for the motion detection in the motion debug window and set the reference frame for every camera.
	/// </summary>
    public partial class MotionDebugWindow : Form
    {
		/// <summary>
		/// The current selected cam.
		/// </summary>
        private static SensorCam curCam = null;

		/// <summary>
		/// Image for the threshold gray box.
		/// </summary>
        private static Image<Gray, Byte> thresholdGrayImage;

		/// <summary>
		/// Default constructor for the motion debug window.
		/// </summary>
        public MotionDebugWindow() {
            InitializeComponent();
        }

		/// <summary>
		/// This method will be called when the motion debug window will be open.
		/// It sets the default values and refreshes the camera list.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void MotionDebugWindow_Load(object sender, EventArgs e) {
            thresholdGrayImage = new Image<Gray, Byte>(120, 40, new Gray(Convert.ToDouble(thresholdUpDown.Value)));
			thresholdGrayBox.Image = thresholdGrayImage;
            refreshCamList();
			selectCamList.SelectedIndex = 0;
			pitchFactorUpDown.Value = Convert.ToDecimal(Program.PitchTransFactor);
			thresholdUpDown.Value = Convert.ToDecimal(Program.PrepThreshold);
			erodeUpDown.Value = Convert.ToDecimal(Program.PrepErode);
			dilateUpDown.Value = Convert.ToDecimal(Program.PrepDilate);

			moveFactorGrayUpDown.Value = Convert.ToDecimal(MotionDetection.MoveFactore);
        }

		/// <summary>
		/// When the window is activated the camera list will be refreshed.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void MotionDebugWindow_Activated(object sender, EventArgs e) {
            refreshCamList();
        }

		/// <summary>
		/// When the window is closing the images update method should not been called again.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void MotionDebugWindow_FormClosing(object sender, FormClosingEventArgs e) {
            curCam.Updated -= updateMotionDebugFrames;
        }

		/// <summary>
		/// Save the values in the xml file.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void MotionDebugWindow_FormClosed(object sender, FormClosedEventArgs e) {
			Program.SaveSettings();
        }

		/// <summary>
		/// Set all images in the motion debug window.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void updateMotionDebugFrames(object sender, EventArgs e) {
			// check if all image boxes are not null befor set the images
			if (refFramGrayBox != null && curFrameBox != null && detectionFrameBox != null && calcFrameGrayBox != null && transCurFrameBox != null) {
				try {
					refFramGrayBox.Image = curCam.RefFrameGray;
					curFrameBox.Image = curCam.Frame;
					detectionFrameBox.Image = curCam.MotionFrame;
					calcFrameGrayBox.Image = curCam.CalcFrameGray;
					transCurFrameBox.Image = curCam.TransFrame;
				} catch (InvalidOperationException) {
					// Sometimes there are problems with disposing the form with his image boxes,
					// so the program tries to set the images although it is not neccessary,
					// that's why i am catching this exception an do nothing.
				}
			}
        }

		/// <summary>
		/// Refreshes the list from all added cameras.
		/// </summary>
        private void refreshCamList() {
            selectCamList.Items.Clear();
            // for loop instead of foreach to assure that indices stay the same
            for (int i = 0; i < Program.Cameras.Count; i++) {
                selectCamList.Items.Add(Program.Cameras[i].Name);
            }
        }

		/// <summary>
		/// Set the current selected camera.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void selectCamList_SelectedIndexChanged(object sender, EventArgs e) {
            if (curCam != null) 
                curCam.Updated -= updateMotionDebugFrames;
            curCam = Program.Cameras.Find(new Predicate<SensorCam>(target => { return (string)selectCamList.SelectedItem == target.Name; }));
            curCam.Updated += updateMotionDebugFrames;
        }

		/// <summary>
		/// Set the reference frame for the selected cam.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void setRefFrameGrayButton_Click(object sender, EventArgs e) {
            curCam.Device.AutoExposure = false;
            curCam.Device.AutoGain = false;
            curCam.Device.AutoWhiteBalance = false;
			Image<Bgr, Byte> tmpRefFrame = curCam.Frame;
			Image<Bgr, Byte> tmpRefFrameWarp = tmpRefFrame.WarpPerspective(curCam.TransMatrix, Program.FrameWidth, Program.FrameHeight, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR, Emgu.CV.CvEnum.WARP.CV_WARP_FILL_OUTLIERS, new Bgr(0, 0, 0));
			curCam.RefFrameGray = tmpRefFrameWarp.Convert<Gray, Byte>();
        }

		/// <summary>
		/// Set the value for the threshold, for all cameras and sets the threshold image.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void thresholdUpDown_ValueChanged(object sender, EventArgs e) {
            Program.PrepThreshold = Convert.ToDouble(thresholdUpDown.Value);
			thresholdGrayImage.SetValue(Program.PrepThreshold);
			thresholdGrayBox.Image = thresholdGrayImage;
        }

		/// <summary>
		/// Set the value for the erode iterations, for all cameras.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void erodeUpDown_ValueChanged(object sender, EventArgs e) {
            Program.PrepErode = Convert.ToInt32(erodeUpDown.Value);
        }

		/// <summary>
		/// Set the value for the dilate iterations, for all cameras.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void dilateUpDown_ValueChanged(object sender, EventArgs e) {
			Program.PrepDilate = Convert.ToInt32(dilateUpDown.Value);
        }

		/// <summary>
		/// Set the value for the move factor, for all cameras.
		/// This should be 0.0 for default.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void moveFactorGrayUpDown_ValueChanged(object sender, EventArgs e) {
            MotionDetection.MoveFactore = Convert.ToDouble(moveFactorGrayUpDown.Value);
        }

		/// <summary>
		/// Set the value for the pixel factor depending on the pitch, for all cameras.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void pitchFactorUpDown_ValueChanged(object sender, EventArgs e) {
			Program.PitchTransFactor = Convert.ToDouble(pitchFactorUpDown.Value);
			curCam.Pitch = curCam.Pitch;
		}

    }
}
